package joc.internal.transformer;

import java.util.List;

import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import joc.Always;
import joc.Invariant;

public class CustomInvariantClassTransformer extends ClassTransformer {

	public CustomInvariantClassTransformer(ClassPool pool) {
		super(pool);
	}

	@Override
	public void transform(CtClass clazz) throws Exception {
		List<CtClass> consultedTypes = getConsultedTypes(clazz);
		consultedTypes.add(clazz);
		for (CtClass consultedType : consultedTypes) {
			if (consultedType.hasAnnotation(Invariant.class)) {
				Invariant annotation = (Invariant) consultedType.getAnnotation(Invariant.class);
				for (Class<?> invClass : annotation.value()) {
					applyInvariant(clazz, invClass.getName());
				}
			}
		}
	}

	private void applyInvariant(CtClass clazz, String invClassName) throws Exception {
		logger.info("applying invariant " + invClassName + " on class " + clazz.getName());
		CtClass annotationClass = pool.get(invClassName);
		String insertBefore = initializeConditionString(invClassName);
		String insertAfter = "";
		for (CtMethod pcMethod : annotationClass.getDeclaredMethods()) {
			if (pcMethod.hasAnnotation(Always.class)) {
				String callConditionString = callConditionString(pcMethod, true);
				insertBefore += callConditionString;
				insertAfter += callConditionString;
			}
		}
		logger.info("insertBefore: " + insertBefore);
		logger.info("insertAfter: " + insertAfter);
		for (CtBehavior behavior : clazz.getDeclaredBehaviors()) {
			if (behavior instanceof CtConstructor) {
				logger.info("at constructor " + behavior.getLongName());
				insertAfter(behavior, insertBefore);
			} else {
				insertAfter(behavior, insertAfter);
				insertBefore(behavior, insertBefore);
			}
		}
	}
}
